#include <homeworkhead.h>
#include "head.h"
#define STR_LEN 8//定义随机输出的字符串长度。
//创建新目录 
int myMkdir(task_t *pTask) 
{
    char *cond = strtok(pTask->buf, " "); //命令，cond中内容应为mkdir
    char *dirName = strtok(NULL, " ");
    MYSQL *db = pTask->db;
    //获得当前路径的目录的id作为新建逻辑目录的preid
    //select id from myNetDisk where phyaddr = 'nowPath';
    char sql[300] = "select id from myNetDisk where phyaddr = '";
    strcat(sql,pTask->nowPath);
    strcat(sql,"';");
    int qret = mysql_query(db, sql);
    //取出id
    MYSQL_RES *result = mysql_store_result(db);
    MYSQL_ROW row = mysql_fetch_row(result);
    //插入这个新建的逻辑目录，preid是之前获得的id，地址往后拼上/自己名字
    //insert into myNetDisk (fileName,user,preid,phyaddr)values('dirName','p->username',row[0],'p->nowPath');
    char newbuf[100] ={0}; //用一个新buf接收新目录逻辑路径，保持nowPath不变
    strcat(newbuf,pTask->nowPath);
    strcat(newbuf,"/");
    strcat(newbuf,dirName);
    char sql2[300] = "insert into myNetDisk (fileName,user,preid,phyaddr)values('";
    strcat(sql2,dirName);
    strcat(sql2,"','");
    strcat(sql2,pTask->usrName);
    strcat(sql2,"',");
    strcat(sql2,row[0]);
    strcat(sql2,",'");
    strcat(sql2,newbuf);
    strcat(sql2,"');");
    puts(sql2);
    qret = mysql_query(db, sql2);
    return 0;
}
//打印当前目录
int printWorkingDir(task_t *pTask)
{   
    send(pTask->netFd,pTask->nowPath,strlen(pTask->nowPath),0);
}
//删除文件
int removeFile(task_t *pTask)
{ //删除目标文件
    // removeFile argv[1]
    // char buf[1024];
    char delim[10] = " ";
    char *token1, *token2;
    int ret;
    token1 = strtok(pTask->buf, delim);
    token2 = strtok(NULL, delim);  //分割出文件名
    struct stat fileinfo;          //定义文件属性结构体存储要删除文件属性
    ret = stat(token2, &fileinfo); //将目标文件属性存入结构体
    while (1)
    {
        if (unlink(token2) < 0)
        {
            printf("RMOVE ERROR!");
            break;
        }
        unlink(token2);
        if (fileinfo.st_nlink == 0)
        { //判断文件上是否还有链接，没有则删除成功
            printf("RMOVE COMPLETED!\n");
            break;
        }
    }
}
//cd切换目录
int cd(task_t *pTask)
{
    char s[1024] = {0};
    strcpy(s, pTask->buf);
    char *w = strtok(s, " ");
    char orderName[512] = {0};
    char destPath[512] = {0};
    strcpy(orderName, w);
    if ((w = strtok(NULL, " ")) != NULL)
    {
        strcpy(destPath, w);
    }

    if (!strcmp(destPath, "-"))
    {
        if (strlen(pTask->prePath) == 0)
        {
            char s[1024] = {0};
            strcpy(s, "cd :OLDPWD未设定");
            send(pTask->netFd, s, strlen(s), 0);
            return -1;
        }
        else
        {
            char temp[1024] = {0};
            strcpy(temp, getcwd(NULL, 0));
            int ret = chdir(pTask->prePath);
            ERROR_CHECK(ret, -1, "chdir");
            strcpy(pTask->prePath, temp);
        }
    }
    else if (strlen(destPath) == 0 || !strcmp(destPath, "~"))
    {
        strcpy(pTask->prePath, getcwd(NULL, 0));
        int ret = chdir("/home");
        ERROR_CHECK(ret, -1, "chdir1");
        DIR *pHome = opendir("/home");
        struct dirent *p;
        char s[1024] = {0};
        while ((p = readdir(pHome)) != NULL)
        {
            if (!strcmp(p->d_name, ".") || !strcmp(p->d_name, ".."))
            {
                continue;
            }
            strcat(s, p->d_name);
            break;
        }
        closedir(pHome);
        ret = chdir(s);
        ERROR_CHECK(ret, -1, "chdir2");
    }
    else
    {
        strcpy(pTask->prePath, getcwd(NULL, 0));
        int ret = chdir(destPath);
        if (ret == -1)
        {
            perror("chdir");
            send(pTask->netFd, "chdir: No such file or directory!", 33, 0);
            return -1;
        }
        // ERROR_CHECK(ret, -1, "chdir");
    }
    char *str = getcwd(NULL, 0);
    // puts(str);
    send(pTask->netFd, str, strlen(str), 0);
}
//ls查看当前目录
int ls(task_t *pTask)
{
    MYSQL *db = pTask->db;
    //获得当前路径的目录的id作为展示文件的preid且user=用户名
    //select id from myNetDisk where phyaddr = 'nowPath';
    char sql[300] = "select id from myNetDisk where phyaddr = '";
    strcat(sql,pTask->nowPath);
    strcat(sql,"';");
    //puts(sql);
    int qret = mysql_query(db, sql);
    //取出id
    MYSQL_RES *result = mysql_store_result(db);
    MYSQL_ROW row = mysql_fetch_row(result);
    mysql_free_result(result);
    //select fileName from myNetDisk where user = '用户名' and preid = row[0];
    char sql2[300] = "select fileName from myNetDisk where user = '";
    strcat(sql2,pTask->usrName);
    strcat(sql2,"' and preid=");
    strcat(sql2,row[0]);
    strcat(sql2,";");
    puts(sql2);
    qret = mysql_query(db, sql2);
    //取出fileName
    MYSQL_RES *result2 = mysql_store_result(db);
    MYSQL_ROW row2;
    while((row2=mysql_fetch_row(result2))!=NULL){
        for(int i = 0;i<mysql_num_fields(result2);++i){
            printf("%s ",row2[i]);
        }
           fflush(stdout);
    }
    mysql_free_result(result2);
}
//传文件给客户端
int downloadFile(task_t *pTask)
{
    // gets file1
    char *command = strtok(pTask->buf, " "); //解析获取命令
    char *fileName = strtok(NULL, " ");      //解析获取文件名
    int length = strlen(fileName);

    //首先判断文件是否存在,文件存在是传输文件的必要条件！
    DIR *pdir = opendir(".");
    ERROR_CHECK(pdir, NULL, "open");
    struct dirent *pdirent;
    while ((pdirent = readdir(pdir)) != NULL){
        if (!strcmp(pdirent->d_name, fileName)){
            break;
        }
    }
    if ((pdirent = readdir(pdir)) == NULL){
        puts("No such file or directory!");
        send(pTask->netFd, "No such file or directory!", 26, 0);
        return -1;
    }

    char pianyi[100] = {0};
    recv(pTask->netFd, pianyi, 100, 0); //接收客户端发来的客户端本地文件的大小，也就是偏移量
    //服务端再把字符串类改回long类型
    long pianyilen = atoi(pianyi); //偏移量也有可能是0
    // printf("pianyilen = %d\n",pianyilen);

    train_t t;
    bzero(t.buf, sizeof(t.buf));
    t.dataLength = length;
    strcpy(t.buf, fileName);

    printf("length = %d,fileName = %s\n", length, fileName);

    int fd = open(fileName, O_RDWR);
    ERROR_CHECK(fd, -1, "open");
    // int ret = send(pTask->netFd,&t,sizeof(t.dataLength)+ t.dataLength,MSG_NOSIGNAL);//发送文件名
    // ERROR_CHECK(ret,-1,"send");
    //不再需要发送文件名，客户端自己获取
    //获取服务器文件总大小并发送
    struct stat statbuf;
    int ret = fstat(fd, &statbuf); //获取文件的总大小
    ERROR_CHECK(ret, -1, "fstat");
    t.dataLength = sizeof(statbuf.st_size); //把文件的大小装入小火车
    memcpy(t.buf, &statbuf.st_size, sizeof(statbuf.st_size));
    send(pTask->netFd, &t, sizeof(t.dataLength) + t.dataLength, MSG_NOSIGNAL);

    //对比进行判断
    long totalFileLen = statbuf.st_size;
    printf("pianyilen = %ld,totallen = %ld\n", pianyilen, totalFileLen);
    if (pianyilen < totalFileLen)
    {
        lseek(fd, pianyilen, SEEK_SET);
    }
    else
    {
        // printf("Recv success!\n");
        return 0;
    }

    char *p = (char *)mmap(NULL, statbuf.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    ERROR_CHECK(p, MAP_FAILED, "mmap");
    off_t total = pianyilen;
    while (total < statbuf.st_size)
    {
        if (statbuf.st_size - total < sizeof(t.buf))
        {
            //最后一次发送
            t.dataLength = statbuf.st_size - total;
        }
        else
        {
            t.dataLength = sizeof(t.buf);
        }
        memcpy(t.buf, p + total, t.dataLength); //把数据装入火车车厢
        int ret = send(pTask->netFd, &t, sizeof(t.dataLength) + t.dataLength, MSG_NOSIGNAL);
        ERROR_CHECK(ret, -1, "send");
        total += t.dataLength;
    }
    munmap(p, statbuf.st_size);
    t.dataLength = 0;
    send(pTask->netFd, &t, sizeof(t.dataLength), 0);
    //发送终止
    close(fd);
}
int recvn(int netFd, void *p, int n)
{
    int total = 0;
    int ret;
    char *pTmp = (char *)p;
    while (total < n)
    {
        ret = recv(netFd, pTmp + total, n - total, 0);
        ERROR_CHECK(ret, -1, "recv");
        total += ret;
    }
    return 0;
}
//接收从客户端上传来的文件
int uploadFile(task_t *pTask)
{
    int dataLength;
    char name[1024] = {0};
    int ret = recvn(pTask->netFd, &dataLength, sizeof(int)); //先收火车头
    ERROR_CHECK(ret, -1, "recv");
    ret = recvn(pTask->netFd, name, dataLength); //再收文件名
    ERROR_CHECK(ret, -1, "recv");
    int fd = open(name, O_RDWR | O_CREAT, 0666);
    ERROR_CHECK(fd, -1, "open");
    char buf[1024] = {0};
    long fileSize;
    recvn(pTask->netFd, &dataLength, sizeof(int));    //先收火车头
    recvn(pTask->netFd, &fileSize, sizeof(fileSize)); //再收文件长度
    printf("fileSize = %ld\n", fileSize);
    // ftruncate(fd,fileSize);
    off_t downSize = 0;
    off_t lastSize = 0;
    off_t slice = fileSize / 1000;
    struct timeval begTime, endTime;
    gettimeofday(&begTime, NULL);
    while (1)
    {
        ret = recvn(pTask->netFd, &dataLength, sizeof(int)); //先收火车头
        ERROR_CHECK(ret, -1, "recv");
        if (dataLength == 0)
        {
            break;
        }
        if (dataLength != 1000)
        {
            printf("dataLength = %d\n", dataLength);
        }
        ret = recvn(pTask->netFd, buf, dataLength);
        ERROR_CHECK(ret, -1, "recv");
        downSize += dataLength;
        if (downSize - lastSize > slice)
        {
            printf("%5.2lf%s\r", 100.0 * downSize / fileSize, "%");
            fflush(stdout);
            lastSize = downSize;
        }
        write(fd, buf, dataLength);
    }
    printf("100.00%s\n", "%");
    gettimeofday(&endTime, NULL);
    printf("total time = %ld\n", 1000000 * (endTime.tv_sec - begTime.tv_sec) + endTime.tv_usec - begTime.tv_usec);
}
//生成随机盐值
void GenerateStr(char *salt)
{
    char str[STR_LEN + 1] = {0};
    int i, flag;
    srand(time(NULL)); //通过时间函数设置随机数种子，使得每次运行结果随机。
    for (i = 0; i < STR_LEN; i++)
    {
        flag = rand() % 3;
        switch (flag)
        {
        case 0:
            str[i] = rand() % 26 + 'a';
            break;
        case 1:
            str[i] = rand() % 26 + 'A';
            break;
        case 2:
            str[i] = rand() % 10 + '0';
            break;
        }
    }
    strcpy(salt,str);
    //printf("salt = %s\n",salt);
}
//拼接盐值
void saltSplice(char *salt){
    char buf[100]={0};
    strcpy(buf,"$6$");
    strcat(buf,salt);
    strcat(buf,"$");
    strcpy(salt,buf);
    //puts(salt);
}
//连接数据库
void mysqlInit(task_t *pTask)
{
    MYSQL *db = mysql_init(NULL);
    pTask->db = db;
    char *host = "localhost";   //写自己本地ip也行
    char *user = "liguohao"; //目前只给root和liguohao两个用户加了mysql权限
    char *password = "1234";
    char *database = "test42";
    MYSQL *ret = mysql_real_connect(db, host, user, password, database, 0, NULL, 0);
    if (ret != NULL){
        // printf("db = %p,ret = %p\n",db,ret);
        strcat(pTask->nowPath,"/");//task获得当前用户逻辑目录路径
        strcat(pTask->nowPath,pTask->usrName);
        printf("db connect success\n");
    }else{
        printf("Error:%s\n", mysql_error(db));
        exit(-1);
    }
}
//注册账号
int mysqlRegister(task_t *pTask)
{
    task_t sqlTask = *pTask;
    MYSQL *db = pTask->db;
    char sPwd[100]={0};
    saltSplice(sqlTask.salt);
    strcpy(sPwd,crypt(sqlTask.usePwd,sqlTask.salt));
    //insert into userInfo values(chao,salt,密文);
    char sql[300] = "insert into userInfo values('";
    strcat(sql,sqlTask.usrName);
    strcat(sql,"','");
    strcat(sql,sqlTask.salt);
    strcat(sql,"','");
    strcat(sql,sPwd);
    strcat(sql,"');");
    puts(sql);
    int qret = mysql_query(db, sql);
    if (qret != 0)
    {
        printf("Error: %s\n", mysql_error(db));
        exit(-1);
    }
    //注册的同时在数据库建一个逻辑目录，与用户名同名，且逻辑地址为/用户名
    //insert into myNetDisk (fileName,user,preid,phyaddr)values('liguohao','liguohao',0,'/liguohao');
    char sql2[300] = "insert into myNetDisk (fileName,user,preid,phyaddr)values('";
    strcat(sql2,sqlTask.usrName);
    strcat(sql2,"','");
    strcat(sql2,sqlTask.usrName);
    strcat(sql2,"',0,'/");
    strcat(sql2,sqlTask.usrName);
    strcat(sql2,"');");
    puts(sql2);
    qret = mysql_query(db, sql2);
    if (qret != 0)
    {
        printf("Error: %s\n", mysql_error(db));
        exit(-1);
    }
    send(sqlTask.netFd,"注册成功！",20,0);
}
//登录账号验证
int mysqlLogin(task_t *pTask){
    task_t sqlTask = *pTask;
    MYSQL *db = pTask->db;
    //select * from userInfo where userName = 'chao';
    //拼接字符串
    char sql[300] = "select salt from userInfo where userName = '";
    strcat(sql,sqlTask.usrName);
    strcat(sql,"';");
    int qret = mysql_query(db, sql);
    if (qret != 0){//如果用户名不对会返回-1
        printf("Error: %s\n", mysql_error(db));
        exit(-1);
    }
    //取出salt
    MYSQL_RES *result = mysql_store_result(db);
    MYSQL_ROW row = mysql_fetch_row(result);
    char sPwd[100] ={0};//通过salt获得密文
    if(row  != NULL){
        //printf("%s\n",row[0]);
        strcpy(sPwd,crypt(sqlTask.usePwd,row[0]));
    }else{
        return -1;
    }
    //puts(sPwd);
    //取得数据库里面的密文
    char sql2[300] = "select secretPwd from userInfo where userName = '";
    strcat(sql2,sqlTask.usrName);
    strcat(sql2,"';");
    //puts(sql2);
    qret = mysql_query(db, sql2);
    MYSQL_RES *result2 = mysql_store_result(db);
    MYSQL_ROW row2 = mysql_fetch_row(result2);
    //puts(row2[0]);
    if(strcmp(sPwd,row2[0])==0)//和从数据库取出的密文比较
    {
        return 0;
    }else{
        return -1;
    }
}